package sicnu.cs.ich.user.rest;

import sicnu.cs.ich.api.common.Constants;
import sicnu.cs.ich.api.common.ResultInfo;
import sicnu.cs.ich.api.common.annotations.OperLog;
import sicnu.cs.ich.api.common.enums.OperEnum;
import sicnu.cs.ich.api.domain.entity.Validate;
import sicnu.cs.ich.api.domain.form.UserForm;
import sicnu.cs.ich.common.util.ResultInfoUtil;
import sicnu.cs.ich.security.domain.entity.User;
import sicnu.cs.ich.security.service.IUserService;
import sicnu.cs.ich.user.service.IValidateService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.*;
import sicnu.cs.ich.common.services.ISendMailService;
import sicnu.cs.ich.common.services.impl.RedisService;
import sicnu.cs.ich.security.util.ValidateUtils;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.Email;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

import static sicnu.cs.ich.api.common.Constants.*;
import static sicnu.cs.ich.security.util.ValidateUtils.isValidEmail;
import static sicnu.cs.ich.security.util.ValidateUtils.isValidStrParams;


/**
 * @author CaiKe
 */
@Slf4j
@RestController
@RequestMapping("/auth/user")
@RequiredArgsConstructor
@Api(tags = {"AUTH-用户相关"})
public class AuthUserResource {
    private final RedisService redisService;
    private final IUserService userService;
    private final ISendMailService sendMailService;
    private final IValidateService validateService;
    private static final String VALIDATE_TYPE = "validateType";
    private static final String VALIDATE_VALUE = "validateValue";
    private final static String TOKEN = "token";
    private static final String PASSWORD = "password";
    private static final String REDIS_EMAIL_LOGIN_CODE = Constants.REDIS_EMAIL_LOGIN_CODE_KEY;

    /**
     * 校验用户名是否存在
     *
     * @param params：type校验类型
     * @return 返回信息
     */
    @ApiOperation(value = "注册接口-1-校验用户名/邮箱/手机号是否存在"
            , notes = "参数：validateType（1-用户名 2-邮箱 3-手机号）、validateValue")
    @PostMapping("/validate")
    public ResultInfo<Void> validate(@RequestBody Map<String, String> params) {
        String type = params.get(VALIDATE_TYPE);
        String value = params.get(VALIDATE_VALUE);
        // 校验参数是否合法
        boolean isValid = StringUtils.isNotBlank(type) && StringUtils.isNotBlank(value)
                && (USERNAME_TYPE.equals(type) || EMAIL_TYPE.equals(type) || MOBILE_TYPE.equals(type));
        if (!isValid) {
            return ResultInfoUtil.buildError("参数校验失败");
        }
        var isExist = false;
        switch (type) {
            case USERNAME_TYPE:
                isExist = userService.isUsernameExisted(value);
                break;
            case EMAIL_TYPE:
                isExist = userService.isEmailExisted(value);
                break;
            case MOBILE_TYPE:
                isExist = userService.isMobileExisted(value);
                break;
            default:
                break;
        }
        return isExist ? ResultInfoUtil.buildError()
                : ResultInfoUtil.buildSuccess();
    }

    @ApiOperation(value = "注册接口-2-发送邮箱验证码")
    @GetMapping("/registerCode")
    public ResultInfo<String> register(@RequestParam String email) {
        if (!isValidEmail(email)) {
            return ResultInfoUtil.buildError("邮箱格式错误");
        }
        // 发送验证码邮件(生成6位随机数)
        String code = ValidateUtils.createValidateCode();
        // 保存验证码
        redisService.cacheValue(Constants.REDIS_EMAIL_CODE + code, email, 60 * 5);
        sendMailService.sendValidateCodeEmail(email, code);
        return ResultInfoUtil.buildSuccess();
    }


    /**
     * 用户注册接口-3-添加用户信息
     *
     * @param user 用户信息
     * @return 是否注册成功
     */
    @ApiOperation(value = "注册接口-3-注册时")
    @PostMapping("/register")
    public ResultInfo<Void> register(@Valid @RequestBody UserForm user) {
        // 校验验证码是否存在或长度是否为6
        if (StringUtils.isBlank(user.getCode())
                ||user.getCode().length()!=6) {
            return ResultInfoUtil.buildError("验证码不能为空");
        }
        String email = (String) redisService.getValue(Constants.REDIS_EMAIL_CODE + user.getCode());
        if (StringUtils.isBlank(email)) {
            return ResultInfoUtil.buildError("验证码错误或已过期");
        } else {
            user.setEmail(email);
        }
        return userService.handleRegister(user);
    }


    @ApiOperation(value = "重置密码-1-申请邮箱重置密码"
            , notes = "返回失败时表示当日发送次数超过上限或者请求过于频繁")
    @GetMapping("/applyRestPassword/{email}")
    public ResultInfo<Void> applyRestPassword(@PathVariable String email, HttpServletRequest request) {
        User user = userService.findUserByEmail(email);
        if (user == null) return ResultInfoUtil.buildError("邮箱不存在");
        if (validateService.sendValidateLimitation(email, EMAIL_RESET_DAY_LIMIT, EMAIL_RESET_INTERVAL)) {
            // 若允许重置密码，则在t_email_retrieve表中插入一行数据，带有token
            Validate validate = Validate.builder()
                    .userId(user.getId())
                    .email(user.getEmail())
                    .resetToken(UUID.randomUUID().toString())
                    .type("passwordReset")
                    .createDate(new Date())
                    .modifyDate(new Date())
                    .build();
            log.info("申请重置密码的用户：{}" + user.getUsername());
            validateService.insertNewResetRecord(validate);
            // 发送邮件
            validateService.sendPasswordResetEmail(request, email, validate.getResetToken());
            return ResultInfoUtil.buildSuccess();
        } else {
            return ResultInfoUtil.buildError("请求过于频繁");
        }
    }

    @PostMapping("/resetPassword")
    @ApiOperation(value = "重置密码-2", notes = "提交Token和重置密码后的密码")
    @OperLog(type = OperEnum.UPDATE)
    public ResultInfo<Void> resetPassword(@RequestBody Map<String, String> param) {
        if (!isValidStrParams(param, TOKEN, PASSWORD)) {
            return ResultInfoUtil.buildError("缺少参数");
        }
        // 通过token找到validate记录
        String token = param.get("token");
        Validate validate = validateService.findUserByResetToken(token);
        ResultInfo<Void> result = null;
        if (validate == null) {
            result = ResultInfoUtil.buildError("无该重置请求");
        } else {
            int interval = 30;
            if (validateService.validateLimitation(validate.getEmail(), Long.MAX_VALUE, interval, token)
                    && userService.resetPassword(validate.getUserId(), param.get("password"))) {
                result = ResultInfoUtil.buildSuccess();
            } else {
                result = ResultInfoUtil.buildError("重置请求过期");
            }
        }
        return result;
    }

    @GetMapping("/email/login")
    @ApiOperation("邮箱登录-1，发送邮箱验证码")
    public ResultInfo<Void> sendLoginEmail(@Valid @Email @RequestParam String email) {
        if (!userService.isEmailExisted(email)) {
            return ResultInfoUtil.buildError("用户不存在");
        }
        String key = REDIS_EMAIL_LOGIN_CODE + email;
        String code = ValidateUtils.createValidateCode(8);
        sendMailService.sendValidateCodeEmail(email, code);
        redisService.cacheValue(key, code, 3 * 60);
        return ResultInfoUtil.buildSuccess();
    }
}
